﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////


#include "IFamilyManager.h"
#include "IParameterDefinition.h"
#include "FilePath.h"
#include "GbmpFileSystem.h"
#include "GmStyleSheetHelper.h"
#include "IParameterValueBool.h"
#include "IParameterValueDisplayString.h"
#include "IParameterValueDouble.h"
#include "IParameterValueInt.h"
#include "IParameterValueString.h"
#include "ParameterType.h"
#include "SharedParaDefineDlg.h"
#include "UiCommonDialog.h"
#include "UnitUniIdentities.h"
#include "pugixml.h"
#include <QtCore/QVariant>
#include <QtCore/qnamespace.h>
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;

namespace gfam
{
    struct FamilyParameterType2ParameterDefinitionMapItem
    {
        FamilyParameterType2ParameterDefinitionMapItem()
            : pst(ParameterStorageType::Invalid)
            , unitTypeId(UNIT(None))
            , ptId(PARAMETER_TYPE(Invalid))
        {
        }
        FamilyParameterType2ParameterDefinitionMapItem(ParameterStorageType pst_, const UniIdentity& utId_, const UniIdentity& ptId_)
            : pst(pst_)
            , unitTypeId(utId_)
            , ptId(ptId_)
        {
        }
        ParameterStorageType pst;
        UniIdentity unitTypeId;
        UniIdentity ptId;
    };

    typedef std::map<gfam::FamilyParameterType, FamilyParameterType2ParameterDefinitionMapItem> FamilyParameterType2ParameterDefinitionMap;
    static FamilyParameterType2ParameterDefinitionMap s_pt2pdMap;

    void InitializeMap()
    {
        s_pt2pdMap[gfam::FamilyParameterType::Length] = FamilyParameterType2ParameterDefinitionMapItem(ParameterStorageType::Double, UNIT(Length), PARAMETER_TYPE(Length));
        s_pt2pdMap[gfam::FamilyParameterType::Angle] = FamilyParameterType2ParameterDefinitionMapItem(ParameterStorageType::Double, UNIT(Angle), PARAMETER_TYPE(Angle));
        s_pt2pdMap[gfam::FamilyParameterType::Bool] = FamilyParameterType2ParameterDefinitionMapItem(ParameterStorageType::Bool, UNIT(None), PARAMETER_TYPE(YesNo));
        s_pt2pdMap[gfam::FamilyParameterType::Double] = FamilyParameterType2ParameterDefinitionMapItem(ParameterStorageType::Double, UNIT(None), PARAMETER_TYPE(Float));
        s_pt2pdMap[gfam::FamilyParameterType::Integer] = FamilyParameterType2ParameterDefinitionMapItem(ParameterStorageType::Int, UNIT(None), PARAMETER_TYPE(Number));
        s_pt2pdMap[gfam::FamilyParameterType::String] = FamilyParameterType2ParameterDefinitionMapItem(ParameterStorageType::String, UNIT(None), PARAMETER_TYPE(Text));
        s_pt2pdMap[gfam::FamilyParameterType::Material] = FamilyParameterType2ParameterDefinitionMapItem(ParameterStorageType::ElementId, UNIT(None), PARAMETER_TYPE(Material));
        s_pt2pdMap[gfam::FamilyParameterType::TextStyle] = FamilyParameterType2ParameterDefinitionMapItem(ParameterStorageType::Custom, UNIT(None), PARAMETER_TYPE(TextStyle));
    }

    bool GetFamilyParameterDefinition(FamilyParameterType fpt, ParameterStorageType& pst, UniIdentity& utId, gcmp::UniIdentity& ptId)
    {
        InitializeMap();

        if (s_pt2pdMap.find(fpt) == s_pt2pdMap.end())
        {
            return false;
        }

        pst = (*s_pt2pdMap.find(fpt)).second.pst;
        utId = (*s_pt2pdMap.find(fpt)).second.unitTypeId;
        ptId = (*s_pt2pdMap.find(fpt)).second.ptId;
        return true;
    }

    std::wstring SharedParaDefineDlg::m_cachedPath = L"";

    SharedParaDefineData::SharedParaDefineData()
        : Name(L"")
        , GroupName(L"")
        , Group(L"")
        , Uid()
        , Desc(L"")
        , UserModifiable(true)
        , UserVisible(true)
        , IsInstance(false)
        , Formula(L"")
        , ValueList(nullptr)
        , ParamType(FamilyParameterType::Invalid)
    {

    }
    SharedParaDefineData::SharedParaDefineData(const SharedParaDefineData& source)
        : Name(source.Name)
        , GroupName(source.GroupName)
        , Uid(source.Uid)
        , Desc(source.Desc)
        , UserModifiable(source.UserModifiable)
        , Group(source.Group)
        , UserVisible(source.UserVisible)
        , IsInstance(source.IsInstance)
        , Formula(source.Formula)
        , ParamType(source.ParamType)
        , ValueList(source.ValueList? source.ValueList->Clone(): nullptr)
    {

    }

    SharedParaDefineData::SharedParaDefineData(SharedParaDefineData&& source)
        : Name(source.Name)
        , GroupName(source.GroupName)
        , Uid(source.Uid)
        , Desc(source.Desc)
        , UserModifiable(source.UserModifiable)
        , Group(source.Group)
        , UserVisible(source.UserVisible)
        , IsInstance(source.IsInstance)
        , Formula(source.Formula)
        , ParamType(source.ParamType)
        , ValueList(TransferOwnership(source.ValueList))
    {

    }

    static FamilyParameterType ConvertToFamilyParameterType(const std::wstring& type) 
    {
        if (type == L"Length") 
        {
            return FamilyParameterType::Length;
        }
        else if (type == L"Angle") 
        {
            return FamilyParameterType::Angle;
        }
        else if (type == L"Double") 
        {
            return FamilyParameterType::Double;
        }
        else if (type == L"Material") 
        {
            return FamilyParameterType::Material;
        }
        else if (type == L"Integer") 
        {
            return FamilyParameterType::Integer;
        }
        else if (type == L"Bool") 
        {
            return FamilyParameterType::Bool;
        }
        else if (type == L"String") 
        {
            return FamilyParameterType::String;
        }
        else if (type == L"Internal") 
        {
            return FamilyParameterType::Internal;
        }
        else if (type == L"Profile") 
        {
            return FamilyParameterType::Profile;
        }
        else 
        {
            DBG_WARN(L"未识别的参数类型",L"paramodel", L"2024-02-20");
            return FamilyParameterType::Invalid;
        }
    }

    static OwnerPtr<IParameterValueStorage> ConvertToParameterValue(ParameterStorageType type, pugi::xml_node& value)
    {
        OwnerPtr<IParameterValueStorage> opStorage = nullptr;
        switch (type)
        {
        case gcmp::ParameterStorageType::Bool:
            opStorage = IParameterValueBool::Create(value.text().as_bool());
            break;
        case gcmp::ParameterStorageType::Int:
            opStorage = IParameterValueInt::Create(value.text().as_int());
            break;
        case gcmp::ParameterStorageType::Double:
            opStorage = IParameterValueDouble::Create(value.text().as_double());

            break;
        case gcmp::ParameterStorageType::String:
            opStorage = IParameterValueString::Create(value.text().as_string());
            break;
        default:
            DBG_WARN(L"暂不支持的值类型", L"paramodel", L"2024-02-20");
            break;
        }
        return opStorage;
    }

    class  ReadCustomParameterDataFromXML
    {
    public:
        static bool Load(const std::wstring& xmlFilePath, std::map<std::wstring, std::vector<OwnerPtr<SharedParaDefineData>>>& vecCustomData)
        {
            vecCustomData.clear();
            pugi::xml_document xmlDoc;
            pugi::xml_parse_result xmlParseResult = xmlDoc.load_file(xmlFilePath.c_str());
            if (xmlParseResult.status == pugi::status_ok)
            {
                std::wstring sParameterGroup = L"Group";

                std::wstring sParameterLabel = L"Param";

                std::wstring sParameterType = L"ParamType";
                std::wstring sParameterUidLabel = L"Guid";
                std::wstring sParameterName = L"Name";
                std::wstring sParameterGroupName = L"GroupName";
                std::wstring sParameterDesc = L"Description";
                std::wstring sParameterVisible = L"UserVisible";
                std::wstring sParameterModifiable = L"UserModifiable";
                std::wstring sParameterInstance = L"Instance";
                std::wstring sParameterFormula = L"Formula";
                std::wstring sParameterEnumList = L"EnumList";
                std::wstring sParameterValueItem = L"Item";

                auto parameterGroupNodeSet = xmlDoc.first_child().children(sParameterGroup.c_str()); //获取所有的组

                for (auto groupNode = parameterGroupNodeSet.begin(); groupNode != parameterGroupNodeSet.end(); ++groupNode)
                {
                    std::wstring parameterGroup = groupNode->attribute(L"Name").as_string(L"default");

                    auto parameterNodeSet = groupNode->children(sParameterLabel.c_str()); //获取组下面所有参数

                    for (auto iter = parameterNodeSet.begin(); iter != parameterNodeSet.end(); ++iter)
                    {
                        //解析参数
                        std::wstring uidString = iter->child(sParameterUidLabel.c_str()).child_value();
                        std::wstring parameterName = iter->child(sParameterName.c_str()).child_value();
                        std::wstring parameterGroupName = iter->child(sParameterGroupName.c_str()).child_value();
                        std::wstring parameterType = iter->child(sParameterType.c_str()).child_value();
                        std::wstring parameterDesc = iter->child(sParameterDesc.c_str()).child_value();
                        std::wstring parameterVisible = iter->child(sParameterVisible.c_str()).child_value();
                        std::wstring parameterModifiable = iter->child(sParameterModifiable.c_str()).child_value();
                        std::wstring parameterInstance = iter->child(sParameterInstance.c_str()).child_value();
                        std::wstring parameterFormula = iter->child(sParameterFormula.c_str()).child_value();

                        OwnerPtr<SharedParaDefineData> data = NEW_AS_OWNER_PTR(SharedParaDefineData);
                        Guid guid = GuidUtils::FromString(uidString);

                        if (!GuidUtils::IsValid(guid))
                        {
                            std::wstring message = L"参数" + parameterName + L"的Uid解析失败 " + uidString + L" ,请检查配置的Uid是否正确？";

                            UiCommonDialog::ShowMessageBox(GBMP_TR(L"错误"), GBMP_TR(message), (int)UiCommonDialog::ButtonType::OK);
                            continue;
                        }

                        data->Uid = UniIdentity(guid);
                        data->Desc = parameterDesc;
                        data->Group = parameterGroup;
                        data->UserVisible = StrICmp(parameterVisible.c_str(), L"False") == 0 ? false : true;
                        data->UserModifiable = StrICmp(parameterModifiable.c_str(), L"False") == 0 ? false : true;
                        data->IsInstance = StrICmp(parameterInstance.c_str(), L"True") == 0 ? true : false;
                        data->Formula = parameterFormula;
                        data->Name = parameterName;
                        data->GroupName = parameterGroupName;
                        data->ParamType = ConvertToFamilyParameterType(parameterType);

                        if (data->ParamType == FamilyParameterType::Invalid)
                            continue;

                        pugi::xml_node enumList = iter->child(sParameterEnumList.c_str());//读取枚举值列表
                        if (data->ParamType != FamilyParameterType::Bool && data->ParamType != FamilyParameterType::Material && !enumList.empty()) //材质与布尔类型不支持多值配置
                        {
                            std::vector<OwnerPtr<gcmp::IParameterValueDisplayString>> parameterValueDisplayStrings;

                            auto valueItems = enumList.children(sParameterValueItem.c_str());

                            for (auto value = valueItems.begin(); value != valueItems.end(); ++value)
                            {
                                FamilyParameterType fpt = data->ParamType;
                                ParameterStorageType pst = ParameterStorageType::Invalid;
                                UniIdentity utId = UNIT(None);
                                UniIdentity ptId = PARAMETER_TYPE(Invalid);
                                GetFamilyParameterDefinition(fpt, pst, utId, ptId);

                                OwnerPtr<IParameterValueStorage> opStorage = ConvertToParameterValue(pst, *value);

                                if (!opStorage)
                                    continue;

                                std::wstring displayString = value->attribute(L"Display").empty() ? value->text().as_string() : value->attribute(L"Display").as_string();

                                parameterValueDisplayStrings.emplace_back(IParameterValueDisplayString::CreateParameterValueDisplayString(TransferOwnership(opStorage), displayString));

                            }

                            data->ValueList = IParameterValueList::CreateParameterStaticValueList(data->Uid, parameterValueDisplayStrings);
                        }

                        if (vecCustomData.find(parameterGroup) == vecCustomData.end())
                        {
                            vecCustomData[parameterGroup] = std::vector<OwnerPtr<SharedParaDefineData>>();
                        }

                        vecCustomData[parameterGroup].emplace_back(TransferOwnership(data));
                    }

                }
                
                return true;

            }
            
            return  false;
        }

    };

    SharedParaDefineDlg::SharedParaDefineDlg(QWidget *parent)
        : QDialog(parent, Qt::FramelessWindowHint)
        , m_pCurParamData(nullptr)
    {
        ui.setupUi(this);

        DBG_WARN_AND_RETURN_VOID_UNLESS(GmStyleSheetHelper::Instance(), L"GmStyleSheetHelper::Instance()为空!", L"paramodel", L"2024-02-20");
        std::wstring styleSheetStr = GmStyleSheetHelper::Instance()->GetCurrentStyleData();
        setStyleSheet(QString::fromStdWString(styleSheetStr));
        ui.gbmp_title->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
        ui.gbmp_title->setIconVisible(false);
        ui.gbmp_title->setMaxButtonVisible(false);
        ui.gbmp_title->setMinButtonVisible(false);
        ui.gbmp_title->setText(QString::fromStdWString(GBMP_TR(L"共享参数")));
        this->setProperty("use_gbmp_title", true);

        connect(ui.gbmp_title, &GmTitleBar::closeRequested, this, &SharedParaDefineDlg::reject);
        connect(ui.gbmp_title, &GmTitleBar::moveRequested, this, &SharedParaDefineDlg::onMoveRequested);
        
        connect(ui.groupComboBox, static_cast<void(QComboBox::*)(const QString &)>(&QComboBox::currentIndexChanged), this, &SharedParaDefineDlg::OnSelectGroup);

        connect(ui.openFileButton, &QPushButton::clicked, this, &SharedParaDefineDlg::OnOpenFileDialog);

        ui.pathLineEdit->setText(QString::fromStdWString(m_cachedPath));

        if (m_cachedPath != L"" && ReadCustomParameterDataFromXML::Load(m_cachedPath, m_customParameterDatas))
        {
            for (auto& item: m_customParameterDatas)
            {
                ui.groupComboBox->addItem(QString::fromStdWString(item.first));
            }
        }

        connect(ui.paraListWidget, &QListWidget::currentRowChanged, this, &SharedParaDefineDlg::OnSelectParameter);
        
    }

    void SharedParaDefineDlg::OnSelectGroup(const QString& text)
    {
        ui.paraListWidget->clear();

        if (ui.groupComboBox->findText(text) != -1 && !m_customParameterDatas.empty())
        {
           std::vector<OwnerPtr<SharedParaDefineData>>& customDatas =  m_customParameterDatas[text.toStdWString()];

           FOR_EACH_INDEX(index, customDatas)
           {
               ui.paraListWidget->insertItem(index, QString::fromStdWString(customDatas[index]->Name));
           }

           ui.paraListWidget->setCurrentRow(0);
        }
    }

    void  SharedParaDefineDlg::OnSelectParameter(int currentRow)
    {
        if (currentRow == -1)
        {
            m_pCurParamData = nullptr;
            return;
        }

        std::wstring groupText = ui.groupComboBox->currentText().toStdWString();
        if (m_customParameterDatas.find(groupText) == m_customParameterDatas.end())
        {
            m_pCurParamData = nullptr;
            return;
        }

        auto& customDatas = m_customParameterDatas[groupText];

        m_pCurParamData = customDatas[currentRow].get();
    }

    void SharedParaDefineDlg::OnOpenFileDialog()
    {

        std::wstring filter = std::wstring(L"文件(*xml)");
        m_cachedPath = UiCommonDialog::ShowOpenFileDialog(GBMP_TR(L"选择XML文件"), gcmp::FileSystem::GetWorkingDirPath(), filter);
        
        if (m_cachedPath.empty())
            return;

        ui.pathLineEdit->setText(QString::fromStdWString(m_cachedPath));

        if (ReadCustomParameterDataFromXML::Load(m_cachedPath, m_customParameterDatas))
        {
            ui.groupComboBox->clear();

            for (auto& item : m_customParameterDatas)
            {
                ui.groupComboBox->addItem(QString::fromStdWString(item.first));
            }
        }
        else
        {
            UiCommonDialog::ShowMessageBox(L"提示", L"解析XML出错！", (int)UiCommonDialog::ButtonType::OK);
        }
    }

    OwnerPtr<SharedParaDefineData> SharedParaDefineDlg::GetSelectParamData()
    {
        int currentRow = ui.paraListWidget->currentRow();

        if (currentRow == -1)
            return nullptr;

        std::wstring currentGroup = ui.groupComboBox->currentText().toStdWString();

        if (m_customParameterDatas.find(currentGroup) == m_customParameterDatas.end())
            return nullptr;

        return NEW_AS_OWNER_PTR(SharedParaDefineData, *m_customParameterDatas[currentGroup][currentRow]);
    }

    SharedParaDefineDlg::~SharedParaDefineDlg()
    {

    }

    void SharedParaDefineDlg::onMoveRequested(const QPoint& cursorPos, const QPoint& offset)
    {
        Q_UNUSED(cursorPos);
        move(pos() + offset);
    }

}

